﻿using RuleID = ProjScan.ScanRuleFixer.Rule.ProjectAssets;

namespace ProjScan.ScanRuleFixer
{
    using UnityEngine;
    using UnityEditor;
    using System.Collections.Generic;
    using System.Reflection;

    class Mat_UselessTex : IRuleFixer<RuleID.Material.Mat_UselessTex>
    {
        public bool Fix(string assetPath)
        {
            try
            {
                Material material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
                if (material == null)
                {
                    Debug.LogError($"Material at path {assetPath} could not be loaded.");
                    return false;
                }

                if (!GetShaderKeywords(material.shader, out var globalKeywords, out var localKeywords))
                {
                    Debug.LogError($"Failed to get shader keywords for {material.shader.name}");
                    return false;
                }

                HashSet<string> shaderKeywords = new HashSet<string>(globalKeywords);
                foreach (var keyword in localKeywords)
                {
                    shaderKeywords.Add(keyword);
                }

                List<string> validKeywords = new List<string>(material.shaderKeywords);
                foreach (var keyword in material.shaderKeywords)
                {
                    if (!shaderKeywords.Contains(keyword))
                    {
                        validKeywords.Remove(keyword);
                    }
                }

                material.shaderKeywords = validKeywords.ToArray();

                HashSet<string> propertyNames = new HashSet<string>();
                int propertyCount = material.shader.GetPropertyCount();
                for (int i = 0; i < propertyCount; i++)
                {
                    propertyNames.Add(material.shader.GetPropertyName(i));
                }

                SerializedObject serializedMaterial = new SerializedObject(material);
                SerializedProperty savedProperties = serializedMaterial.FindProperty("m_SavedProperties");
                SerializedProperty texEnvs = savedProperties.FindPropertyRelative("m_TexEnvs");
                SerializedProperty floats = savedProperties.FindPropertyRelative("m_Floats");
                SerializedProperty colors = savedProperties.FindPropertyRelative("m_Colors");

                RemoveUnusedProperties(texEnvs, propertyNames);
                RemoveUnusedProperties(floats, propertyNames);
                RemoveUnusedProperties(colors, propertyNames);

                serializedMaterial.ApplyModifiedProperties();
                Debug.Log($"Material at path {assetPath} has been cleaned up.");
            }
            catch (System.Exception e)
            {
                Debug.LogError($"Error fixing material at path {assetPath}: {e}");
                return false;
            }

            return true;
        }

        private void RemoveUnusedProperties(SerializedProperty propertyArray, HashSet<string> validPropertyNames)
        {
            for (int i = propertyArray.arraySize - 1; i >= 0; i--)
            {
                SerializedProperty property = propertyArray.GetArrayElementAtIndex(i);
                string propertyName = property.displayName;

                if (!validPropertyNames.Contains(propertyName))
                {
                    propertyArray.DeleteArrayElementAtIndex(i);
                }
            }
        }

        private bool GetShaderKeywords(Shader shader, out string[] globalKeywords, out string[] localKeywords)
        {
            try
            {
                MethodInfo getGlobalKeywords = typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords",
                    BindingFlags.Static | BindingFlags.NonPublic);
                globalKeywords = (string[])getGlobalKeywords.Invoke(null, new object[] { shader });

                MethodInfo getLocalKeywords = typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords",
                    BindingFlags.Static | BindingFlags.NonPublic);
                localKeywords = (string[])getLocalKeywords.Invoke(null, new object[] { shader });

                return true;
            }
            catch
            {
                globalKeywords = localKeywords = null;
                return false;
            }
        }
    }
}